/*
 * Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wso2.carbon.bpel.ode.integration.store;

import org.apache.axis2.clustering.ClusteringFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.compiler.api.CompilationException;
import org.apache.ode.bpel.dd.DeployDocument;
import org.apache.ode.bpel.dd.TDeployment;
import org.apache.ode.bpel.dd.TProvide;
import org.apache.ode.bpel.iapi.ContextException;
import org.apache.ode.bpel.iapi.ProcessConf;
import org.apache.ode.bpel.iapi.ProcessState;
import org.apache.ode.store.DeploymentUnitDAO;
import org.apache.ode.store.DeploymentUnitDir;
import org.apache.ode.store.ProcessConfDAO;
import org.wso2.carbon.application.deployer.AppDeployerUtils;
import org.wso2.carbon.bpel.BPELConstants;
import org.wso2.carbon.bpel.internal.BPELServiceComponent;
import org.wso2.carbon.bpel.ode.integration.store.clustering.BPELPackageUndeployedCommand;
import org.wso2.carbon.bpel.ode.integration.store.clustering.BPELProcessStateChangedCommand;
import org.wso2.carbon.bpel.ode.integration.store.clustering.NewBPELPackageDeployedCommand;
import org.wso2.carbon.bpel.ode.integration.store.repository.BPELPackageInfo;
import org.wso2.carbon.bpel.ode.integration.store.repository.BPELPackageRepository;
import org.wso2.carbon.bpel.ode.integration.store.repository.BPELPackageRepositoryUtils;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.utils.RegistryClientUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import javax.xml.namespace.QName;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Tenant specific process store.
 * Contains all the information about tenant's BPEL packages and processes.
 */
public class TenantProcessStoreImpl implements TenantProcessStore {
    private static final Log log = LogFactory.getLog(TenantProcessStoreImpl.class);

    // ID of the tenant who owns this process store
    private Integer tenantId;

    // Tenant's Configuration context
    private ConfigurationContext tenantConfigContext;

    // Tenant's configuration registry
    private Registry tenantConfigRegistry;

    // Parent process store
    private ProcessStoreImpl parentProcessStore;

    // BPEL Deployment units available for this tenant
    private final Map<String, DeploymentUnitDir> deploymentUnits =
            new ConcurrentHashMap<String, DeploymentUnitDir>();

    // BPEL Processes in this tenant
    private final Map<QName, ProcessConfigurationImpl> processes =
            new ConcurrentHashMap<QName, ProcessConfigurationImpl>();

    private final Map<String, List<QName>> processesInDeploymentUnit =
            new ConcurrentHashMap<String, List<QName>>();

    private BPELPackageRepository repository;

    // Tenant's BPEL deployment unit repository
    private File bpelDURepo;

    // Axis2 bpel deployment repository where we put BPEL archive artifacts
    private File bpelArchiveRepo;


    public TenantProcessStoreImpl(ConfigurationContext configContext, ProcessStoreImpl parent)
            throws RegistryException {
        tenantConfigContext = configContext;
        tenantId = MultitenantUtils.getTenantId(configContext);
        tenantConfigRegistry =
                BPELServiceComponent.getRegistryService().getConfigSystemRegistry(tenantId);
        parentProcessStore = parent;
    }

    public void init() throws Exception {
        bpelDURepo = new File(parentProcessStore.getLocalDeploymentUnitRepo(), tenantId.toString());
        if (!bpelDURepo.exists()) {
            if (!bpelDURepo.mkdirs()) {
                log.warn("Cannot create tenant " + tenantId + " BPEL deployment unit repository.");
            }
        }
        repository = new BPELPackageRepository(tenantConfigRegistry, bpelDURepo, bpelArchiveRepo);
        repository.init();
    }

    public void handleNewBPELPackageDeploymentNotification(String bpelPackageName) {
        // Copy archive to file system
        // Write the deployment logic to handle that.

        try {
            repository.restoreBPELArchive(repository.getBPELPackageInfo(
                                                      BPELConstants.REG_PATH_OF_BPEL_PACKAGES +
                                                                bpelPackageName));
        } catch (Exception e) {
            log.error("Error occurred while deploying: " + bpelPackageName, e);
        }
    }

    public void handleBPELPackageUndeploymentNotification(String bpelPackageName,
                                                          List<String> versionsOfPackage) {
        updateLocalInstanceWithUndeployment(bpelPackageName, versionsOfPackage);
    }

    public void handleBPELProcessStateChangedNotification(QName pid, ProcessState processState) {
        if (log.isDebugEnabled()) {
            log.debug("Changing state of the process " + pid + " to " +
                       processState);
        }
        if (!isProcessExist(pid)) {
            String errMsg = "Process " + pid + " not found. Process state change failed.";
            log.error(errMsg);
            return;
        }

        if (processState == null) {
            String errMessage = "Process State cannot be null. Process state change failed";
            log.error(errMessage);
            return;
        }

        parentProcessStore.updateLocalInstanceWithStateChange(pid, processState);
    }

    /**
     * Deploy processes in given BPEL archive in ODE.
     * <p/>
     * Deployment flow:
     * - Get the current version from ODE(This version number is global to ODE.)
     * - Create the deployment context(@see BPELDeploymentContext)
     * - Check for existing BPEL archives with the same name(use when updating BPEL packages)
     * - If this is a new deployment or update, extract the archive and deploy processes
     * - If this is a reload(at startup) handle the reload.
     * <p/>
     * Process Versioning:
     * Process version is a single, sequentially incremented number. All deployed packages share
     * the same sequence. All processes in a bundle share the same version number and it's
     * the number of their bundle.
     *
     * @param bpelArchive BPEL package directory
     * @throws RegistryException
     */
    public void deploy(File bpelArchive) throws Exception {
        log.info("Deploying BPEL archive: " + bpelArchive.getAbsolutePath());

        long versionForThisDeployment = parentProcessStore.getCurrentVersion();

        // TODO: fix deployment context to use files.
        BPELDeploymentContext deploymentContext =
                new BPELDeploymentContext(
                                tenantId,
                                parentProcessStore.getLocalDeploymentUnitRepo().getAbsolutePath(),
                                bpelArchive,
                                versionForThisDeployment);
        boolean isExistingPackage = repository.isExistingBPELPackage(deploymentContext);
        boolean isLoadOnly = repository.isBPELPackageReload(deploymentContext);

        if (isExistingPackage) {
            if (isLoadOnly) {
                reloadExistingVersionsOfBPELPackage(deploymentContext);
                // attach this bpel archive with cApp
                attachWithCapp(deploymentContext.getArchiveName(),
                               deploymentContext.getBpelPackageName(), tenantId);
                return; // Once we finish reloading exit from the normal flow.
            } // Else this is a update of existing BPEL package
        }

        try {
            Utils.extractBPELArchive(deploymentContext);
        } catch (Exception e) {
            String errMsg = "Error extracting BPEL archive " + deploymentContext.getBpelArchive()
                    + ".";
            deploymentContext.setDeploymentFailureCause(errMsg);
            deploymentContext.setStackTrace(e);
            handleDeploymentError(deploymentContext);
            return; // Exist from the normal flow on extraction error.
        }

        if (!validateBPELPackage(deploymentContext, isExistingPackage)) {
            handleDeploymentError(deploymentContext);
            return; // Exist from the normal flow on BPEL package validation error.
        }

        deployBPELPackageInODE(deploymentContext);

        if (isExistingPackage) {
            repository.handleBPELPackageUpdate(deploymentContext);
        } else {
            repository.handleNewBPELPackageAddition(deploymentContext);
        }

        // attach this bpel archive with cApp
        attachWithCapp(deploymentContext.getArchiveName(),
                       deploymentContext.getBpelPackageName(), tenantId);

        parentProcessStore.sendProcessDeploymentNotificationsToCluster(
               new NewBPELPackageDeployedCommand(deploymentContext.getBpelPackageName(), tenantId));

    }

    /**
     * Undeploying BPEL package.
     *
     * @param bpelPackageName Name of the BPEL package which going to be undeployed
     */
    public void undeploy(String bpelPackageName) throws RegistryException, ClusteringFault {
        if (log.isDebugEnabled()) {
            log.debug("Undeploying BPEL package " + bpelPackageName + " ....");
        }

        if (!repository.isExistingBPELPackage(bpelPackageName)) {
            // This can be a situation where we undeploy the archive through management console,
            // so that, the archive is deleted from the repo. As a result this method get invoked.
            // to handle this case we just log the message but does not throw an exception.
            final String warningMsg = "Cannot find BPEL package with name " + bpelPackageName +
                    " in the repository. If the bpel package is undeployed through the management" +
                    " console or if this node is a member of a cluster, please ignore this warning.";
            log.warn(warningMsg);
            return;
        }

        List<String> versionsOfThePackage;

        try {
            versionsOfThePackage = repository.getAllVersionsForPackage(bpelPackageName);
        } catch (RegistryException re) {
            String errMessage = "Cannot get all versions of the package " + bpelPackageName
                    + " from registry.";
            log.error(errMessage);
            throw re;
        }

        for (String nameWithVersion : versionsOfThePackage) {
            parentProcessStore.deleteDeploymentUnitDataFromDB(nameWithVersion);
            Utils.deleteInstances(getProcessesInPackage(nameWithVersion));
            for (QName pid : getProcessesInPackage(nameWithVersion)) {
                ProcessConfigurationImpl processConf =
                        (ProcessConfigurationImpl)getProcessConfiguration(pid);
                // This property is read when we removing the axis service for this process.
                // So that we can decide whether we should persist service QOS configs
                processConf.setUndeploying(true);
            }
        }

        try {
            repository.handleBPELPackageUndeploy(bpelPackageName);
        } catch (RegistryException re) {
            String errMessage = "Cannot update the BPEL package repository for undeployment of" +
                    "package " + bpelPackageName + ".";
            log.error(errMessage);
            throw re;
        }

        updateLocalInstanceWithUndeployment(bpelPackageName, versionsOfThePackage);

        parentProcessStore.sendProcessDeploymentNotificationsToCluster(
               new BPELPackageUndeployedCommand(versionsOfThePackage, bpelPackageName, tenantId));

    }

    /**
     * Update the local instance of the BPS server regarding the undeployment of the bpel package.
     * @param bpelPackageName Name of the BPEL package
     * @param versionsOfThePackage List of deployed versions of the package
     */
    public void updateLocalInstanceWithUndeployment(String bpelPackageName,
                                                    List<String> versionsOfThePackage) {
        // Delete the package from the file repository
        deleteBpelArchive(bpelPackageName);
        Collection<QName> undeployedProcesses = new ArrayList<QName>();

        for (String nameWithVersion : versionsOfThePackage) {
            undeploySpecificVersionOfBPELPackage(nameWithVersion, undeployedProcesses);
        }

        parentProcessStore.updateMapsAndFireStateChangeEventsForUndeployedProcesses(tenantId,
                bpelPackageName, undeployedProcesses);
    }

    /**
     * Delete BPEL Archive from the BPEL repository of the file system.
     *
     * @param bpelPackageName Name of the BPEL package
     */
    private void deleteBpelArchive(String bpelPackageName) {
        String bpelArchiveLocation = tenantConfigContext.getAxisConfiguration().getRepository().
                getPath() + File.separator + BPELConstants.BPEL_REPO_DIRECTORY + File.separator +
                bpelPackageName + "." + BPELConstants.BPEL_PACKAGE_EXTENSION;
        log.info("Undeploying BPEL package " + bpelPackageName + ". Deleting BPEL archive " +
                bpelArchiveLocation + "....");
        File bpelArchive = new File(bpelArchiveLocation);
        if (bpelArchive.exists()) {
            if (!bpelArchive.delete()) {
                //For windows
                bpelArchive.deleteOnExit();
            }
        } else {
            log.warn("BPEL archive " + bpelArchive.getAbsolutePath() +
                    " not found. This can happen if you delete " +
                    "the BPEL archive from the file system.");
        }
    }

    private void undeploySpecificVersionOfBPELPackage(final String packageName,
                                                      final Collection<QName> undeployedProcesses) {
        DeploymentUnitDir du = deploymentUnits.remove(packageName);
        processesInDeploymentUnit.remove(packageName);
        if (du != null) {
            long version = du.getVersion();
            for (QName name : du.getProcessNames()) {
                QName pid = Utils.toPid(name, version);
                undeployedProcesses.add(pid);
                processes.remove(pid);
            }
        }
    }

    public void handleTenantUnload() {
    }

    public void hydrate() {
    }

    public ProcessConf getProcessConfiguration(QName pid) {
        return processes.get(pid);
    }

    public void setState(QName pid, ProcessState processState) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Changing state of the process " + pid + " to " + processState);
        }
        if (!isProcessExist(pid)) {
            String errMsg = "Process " + pid + " not found.";
            log.error(errMsg);
            // TODO : Introduce hierarchical exceptions to ODE integration layer.
            throw new Exception(errMsg);
        }

        if (processState == null) {
            String errMessage = "Process State cannot be null.";
            log.error(errMessage);
            throw new Exception(errMessage);
        }

        parentProcessStore.setState(pid, processState);

        parentProcessStore.sendProcessDeploymentNotificationsToCluster(
                       new BPELProcessStateChangedCommand(pid, processState, tenantId));
    }

    private Boolean isProcessExist(QName pid) {
        return processes.containsKey(pid);
    }

    public BPELPackageRepository getBPELPackageRepository() {
        return repository;
    }

    public Map<QName, ProcessConfigurationImpl> getProcesses() {
        return processes;
    }

    public ProcessConf removeProcessConfiguration(QName pid) {
        return processes.remove(pid);
    }

    public List<QName> getProcessesInPackage(String packageName) {
        return processesInDeploymentUnit.get(packageName);
    }

    public Boolean containsProcess(QName pid) {
        return processes.containsKey(pid);
    }

    public void setBpelArchiveRepo(File bpelArchiveRepo) {
        this.bpelArchiveRepo = bpelArchiveRepo;
    }

    /**
     * Log and store the information to registry on BPEL deployment error.
     *
     * @param deploymentContext information about current deployment
     * @throws RegistryException on error accessing registry for persisting information.
     */
    private void handleDeploymentError(BPELDeploymentContext deploymentContext)
            throws Exception {
        if (deploymentContext.getStackTrace() != null) {
            log.error(deploymentContext.getDeploymentFailureCause(),
                    deploymentContext.getStackTrace());
        } else {
            log.error(deploymentContext.getDeploymentFailureCause());
        }
        repository.handleBPELPackageDeploymentError(deploymentContext);
    }

    /**
     * Reload old versions of BPEL package. This is used to handle restart of BPEL server.
     * At restart based on the last modified time of the BPEL archives we'll reload all the versions
     * of that BPEL archive.
     *
     * @param deploymentContext information about current deployment
     * @throws RegistryException on error loading resources from registry.
     */
    private void reloadExistingVersionsOfBPELPackage(BPELDeploymentContext deploymentContext)
            throws RegistryException {
        BPELPackageInfo bpelPackage = repository.getBPELPackageInfo(deploymentContext);
        for (String packageName : bpelPackage.getAvailableVersions()) {
            loadExistingBPELPackage(packageName);
        }
    }

    /**
     * Deploy BPEL package in ODE and add process configuration objects to necessary maps in process
     * store.
     *
     * @param deploymentContext information about current deployment
     * @throws Exception in case of duplicate deployment unit or if error occurred during deploying package in ODE
     */
    private void deployBPELPackageInODE(BPELDeploymentContext deploymentContext) throws Exception {
        File bpelPackage = deploymentContext.getBPELPackageContent();
        log.info("Starting deployment of processes from directory "
                + bpelPackage.getAbsolutePath());

        final Date deployDate = new Date();
        // Create the DU and compile/scan it before doing any other work.
        final DeploymentUnitDir du = new DeploymentUnitDir(bpelPackage);
        // Before coming to this stage, we create the bpel package directory with the static version
        // so we don't need to get the version from database. We can directly use static version
        // calculated from bpel package directory name.
        du.setVersion(du.getStaticVersion());

        try {
            du.compile();
        } catch (CompilationException ce) {
            String logMessage = "Deployment failed due to compilation issues.";
            log.error(logMessage, ce);
            throw new BPELDeploymentException(logMessage, ce);
        }

        du.scan();
        DeployDocument dd = du.getDeploymentDescriptor();
        List<ProcessConfigurationImpl> processConfs = new ArrayList<ProcessConfigurationImpl>();
        List<QName> processIds = new ArrayList<QName>();

        if (deploymentUnits.containsKey(du.getName())) {
            String logMessage = "Aborting deployment. Duplicate Deployment unit "
                    + du.getName() + ".";
            log.error(logMessage);
            throw new BPELDeploymentException(logMessage);
        }

        // Validate BPEL package partially before retiring old versions.
        validateBPELPackage(du);

        // Before updating a BPEL package we need to retire processes in old version
        retirePreviousPackageVersions(du);

        for (TDeployment.Process processDD : dd.getDeploy().getProcessList()) {
            QName processId = Utils.toPid(processDD.getName(), du.getVersion());

            processConfs.add(new ProcessConfigurationImpl(processDD,
                    du,
                    deployDate,
                    parentProcessStore.getEndpointReferenceContext(),
                    tenantConfigContext));
            processIds.add(processId);
        }

        deploymentUnits.put(du.getName(), du);
        processesInDeploymentUnit.put(du.getName(), processIds);
        for (ProcessConfigurationImpl processConf : processConfs) {
            processes.put(processConf.getProcessId(), processConf);
            deploymentContext.addProcessId(processConf.getProcessId());
        }
        try {
            parentProcessStore.onBPELPackageDeployment(
                 tenantId,
                 du.getName(),
                 BPELPackageRepositoryUtils.getResourcePathForBPELPackageContent(deploymentContext),
                 processConfs);
        } catch (ContextException ce) {
            handleDeploymentErrorsAtODELayer(deploymentContext, du.getName());
            deploymentContext.setDeploymentFailureCause("BPEL Package deployment failed at " +
                    "ODE layer. Possible cause: " + ce.getMessage());
            deploymentContext.setStackTrace(ce);
            try {
                handleDeploymentError(deploymentContext);
            } catch (Exception e) {
                e.initCause(ce);
                throw e;
            }
            throw ce;
        }
    }

    /**
     * Check whether processes in this package are already available in the process store or check
     * whether processes are correctly compiled.
     *
     * @param du BPEL deployment unit
     * @throws BPELDeploymentException if there's a error in BPEL package
     */
    private void validateBPELPackage(DeploymentUnitDir du)
            throws BPELDeploymentException{
        DeployDocument dd = du.getDeploymentDescriptor();
        for(TDeployment.Process processDD : dd.getDeploy().getProcessList()){
            QName processId = Utils.toPid(processDD.getName(), du.getVersion());
            if (processes.containsKey(processId)) {
                String logMessage = "Aborting deployment. Duplicate process ID " + processId + ".";
                log.error(logMessage);
                throw new BPELDeploymentException(logMessage);
            }

            QName processType = Utils.getProcessType(processDD);

            DeploymentUnitDir.CBPInfo cbpInfo = du.getCBPInfo(processType);
            if (cbpInfo == null) {
                //removeDeploymentArtifacts(deploymentContext, du);
                String logMessage = "Aborting deployment. Cannot find Process definition for type "
                        + processType + ".";
                log.error(logMessage);
                throw new BPELDeploymentException(logMessage);
            }
        }
    }

    private void handleDeploymentErrorsAtODELayer(BPELDeploymentContext deploymentContext,
                                                  String duName) {
        deploymentUnits.remove(duName);
        processesInDeploymentUnit.remove(duName);
        for (QName pid : deploymentContext.getProcessIdsForCurrentDeployment()) {
            processes.remove(pid);
        }

    }

    private void loadExistingBPELPackage(String bpelPackageName) {
        DeploymentUnitDAO duDAO = parentProcessStore.getDeploymentUnitDAO(bpelPackageName);
        if (duDAO == null) {
            String errMsg = "Cannot find DeploymentUnitDAO instance for package "
                    + bpelPackageName + ".";
            log.error(errMsg);
            throw new BPELDeploymentException(errMsg);
        }
        File bpelPackage = findBPELPackageInFileSystem(duDAO);
        if (bpelPackage == null || !bpelPackage.exists()) {
            throw new BPELDeploymentException("Deployed directory " +
                    (bpelPackage == null ? "(unknown)" : bpelPackage) + " no longer there!");
        }

        DeploymentUnitDir du = new DeploymentUnitDir(bpelPackage);
        du.setVersion(du.getStaticVersion());
        du.scan();

        List<ProcessConfigurationImpl> loaded = new ArrayList<ProcessConfigurationImpl>();
        List<QName> processIds = new ArrayList<QName>();

        for (ProcessConfDAO pConfDAO : duDAO.getProcesses()) {
            TDeployment.Process processDD = du.getProcessDeployInfo(pConfDAO.getType());
            if (processDD == null) {
                log.warn("Cannot load " + pConfDAO.getPID() + "; cannot find descriptor.");
                continue;
            }

            // TODO: update the props based on the values in the DB.

            ProcessConfigurationImpl pConf = new ProcessConfigurationImpl(processDD,
                    du,
                    duDAO.getDeployDate(),
                    parentProcessStore.getEndpointReferenceContext(),
                    tenantConfigContext);
            pConf.setState(pConfDAO.getState());
            processIds.add(pConfDAO.getPID());
            processes.put(pConf.getProcessId(), pConf);
            loaded.add(pConf);
        }

        deploymentUnits.put(du.getName(), du);
        processesInDeploymentUnit.put(du.getName(), processIds);
        parentProcessStore.onBPELPackageReload(tenantId, du.getName(), loaded);
    }

    private File findBPELPackageInFileSystem(DeploymentUnitDAO dudao) {
        String duName = dudao.getName();
        // Done: Fix the logic to handle registry
        log.info("Looking for BPEL package in file system for deployment unit " + duName);

        File bpelDUDirectory = new File(bpelDURepo, duName);
        if (bpelDUDirectory.exists()) {
            return bpelDUDirectory;
        } else {
            String registryCollectionPath = dudao.getDeploymentUnitDir();
            try {
                if (tenantConfigRegistry.resourceExists(registryCollectionPath)) {
                    if (!bpelDUDirectory.exists()) {
                        if (!bpelDUDirectory.mkdirs()) {
                            String errMsg = "Error creating BPEL deployment unit repository for " +
                                    "tenant " + tenantId;
                            log.error(errMsg);
                            log.error("Failed to load BPEL deployment unit " + duName +
                                    " due to above error.");
                            throw new BPELDeploymentException(errMsg);
                        }
                    }
                    RegistryClientUtils.exportFromRegistry(bpelDUDirectory, registryCollectionPath,
                            tenantConfigRegistry);
                    return bpelDUDirectory;
                } else {
                    String errMsg = "Expected resource: " + registryCollectionPath +
                            " not found in the registry";
                    log.error(errMsg);
                    throw new BPELDeploymentException(errMsg);
                }
            } catch (RegistryException re) {
                String errMsg = "Error while exporting deployment unit: " + duName +
                        " to file system from the registry.";
                log.error(errMsg, re);
                throw new BPELDeploymentException(errMsg, re);
            }
        }
    }


    /**
     * Retire all the other versions of the same DU:
     * first take the DU name and insert version regexp,
     * than try to match the this string against names of already deployed DUs.
     * For instance if we are deploying DU "AbsenceRequest-2/AbsenceRequest.ode" and
     * there's already version 2 than regexp
     * "AbsenceRequest([-\\.](\d)+)?/AbsenceRequest.ode" will be matched against
     * "AbsenceRequest-2/AbsenceRequest.ode" and setRetirePackage() will be called accordingly.
     *
     * @param du DeploymentUnitDir object containing in-memory representation of BPEL package. 
     */
    private void retirePreviousPackageVersions(DeploymentUnitDir du) {
        //retire all the other versions of the same DU
        String[] nameParts = du.getName().split("/");
        /* Replace the version number (if any) with regexp to match any version number */
        nameParts[0] = nameParts[0].replaceAll("([-\\Q.\\E](\\d)+)?\\z", "");
        nameParts[0] += "([-\\Q.\\E](\\d)+)?";
        StringBuilder duNameRegExp = new StringBuilder(du.getName().length() * 2);
        for (int i = 0, n = nameParts.length; i < n; i++) {
            if (i > 0) {
                duNameRegExp.append("/");
            }
            duNameRegExp.append(nameParts[i]);
        }

        Pattern duNamePattern = Pattern.compile(duNameRegExp.toString());
        for (String deployedDUname : deploymentUnits.keySet()) {
            Matcher matcher = duNamePattern.matcher(deployedDUname);
            if (matcher.matches()) {
                parentProcessStore.setRetiredPackage(deployedDUname, true);
            }
        }
    }

    private boolean validateBPELPackage(BPELDeploymentContext bpelDeploymentContext,
                                        boolean isExistingPackage) {
        DeploymentUnitDir du;
        try {
            du = new DeploymentUnitDir(bpelDeploymentContext.getBPELPackageContent());
        } catch (IllegalArgumentException e) {
            bpelDeploymentContext.setDeploymentFailureCause(e.getMessage());
            bpelDeploymentContext.setStackTrace(e);
            return false;
        }

        if (!isExistingPackage) {
            DeployDocument deployDocument = du.getDeploymentDescriptor();
            List<TDeployment.Process> processList = deployDocument.getDeploy().getProcessList();
            for (TDeployment.Process process : processList) {
                List<TProvide> provideList = process.getProvideList();
                for (TProvide provide : provideList) {
                    if (getDeployedServices().containsKey(provide.getService().getName())) {
                        String errMsg = "Service: " + provide.getService().getName() + " already " +
                                "used by another process. Try again with a different " +
                                "service name";
                        bpelDeploymentContext.setDeploymentFailureCause(errMsg);
                        return false;
                    }
                }
            }
        }
        return true;

    }


    /**
     * Current bpel package can be coming from a cApp. If that is the case, we have to attach
     * this process with its owner cApp.
     *
     * @param bpelArchiveName - file name of the BPEL package
     * @param bpelPackageName - package name extracted out of the archive name
     * @param tenantId        - current tenant id
     */
    private void attachWithCapp(String bpelArchiveName, String bpelPackageName, int tenantId) {
        // attach with cApp
        AppDeployerUtils.attachArtifactToOwnerApp(bpelArchiveName,
                                                  BPELConstants.BPEL_TYPE,
                                                  bpelPackageName, tenantId);
    }

    public Map<QName, Object> getDeployedServices() {
        return parentProcessStore.getServicesPublishedByTenant(tenantId);
    }

}
